﻿#ifndef HONGMUTEX_H
#define HONGMUTEX_H

#include "HongAssert.h"
#include <iostream>
#include <cstring>

#ifdef SELECT_WINDOWS
    #include <windows.h>
#else
    #include <unistd.h>
    #include <pthread.h>
#endif

namespace Hong{
#ifdef SELECT_WINDOWS
class  Mutex
{
public:
    Mutex() { InitializeCriticalSection(&_critSection); }
    ~Mutex() { DeleteCriticalSection(&_critSection); }
    void Lock() const { EnterCriticalSection(&_critSection); }
    void Unlock() const { LeaveCriticalSection(&_critSection); }

private:
    Mutex(const Mutex&);
    const Mutex &operator = (const Mutex&);

private:
    mutable CRITICAL_SECTION _critSection;
};

#ifndef ETIMEDOUT
#define ETIMEDOUT WAIT_TIMEOUT
#endif

class  CondMutex
{
public:
    CondMutex()
    {
        m_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
        WIN_ASSERT(0 != m_cond);
    }

    ~CondMutex()
    {
        BOOL ret = CloseHandle(m_cond);
        WIN_ASSERT(TRUE == ret);
    }

    int WaitWithTimeout(Mutex *mutex, int mill) const
    {
        mutex->Unlock();
        DWORD ret = WaitForSingleObject(m_cond, mill);
        mutex->Lock();
        if (WAIT_OBJECT_0 == ret)
        {
            return 0;
        }
        else if (WAIT_TIMEOUT == ret)
        {
            return ETIMEDOUT;
        }

        WIN_ASSERT(0);

        return ret;
    }

    int Wait(Mutex *mutex) const
    {
        mutex->Unlock();
        DWORD ret = WaitForSingleObject(m_cond, INFINITE);
        mutex->Lock();
        if (WAIT_OBJECT_0 == ret)
        {
            return 0;
        }

        WIN_ASSERT(0);

        return ret;
    }

    void Signal() const
    {
        BOOL ret = SetEvent(m_cond);
        WIN_ASSERT(TRUE == ret);
    }

private:
    HANDLE m_cond;
};

#else
class Mutex
{
public:
    Mutex()
    {
        m_count = 0;
        m_owner = 0;
        int ret = pthread_mutex_init(&m_mutex, NULL);
        POSIX_ASSERT(ret);
    }

    ~Mutex()
    {
        int ret = pthread_mutex_destroy(&m_mutex);
        POSIX_ASSERT(ret);
    }

    /**
      *@brief 本线程自己多次加锁，不做操作，只增加计数
      *       不会引起死锁
    */
    void Lock() const
    {
        pthread_t self = pthread_self();
        if (m_owner != self)
        {
            int ret = pthread_mutex_lock(&m_mutex);
            POSIX_ASSERT(ret);
            m_owner = self;
        }
        m_count++;
    }

    void Unlock() const
    {
        BASE_ASSERT(pthread_self() == m_owner);
        if (--m_count == 0)
        {
            m_owner = 0;
            int ret = pthread_mutex_unlock(&m_mutex);
            POSIX_ASSERT(ret);
        }
    }

    pthread_mutex_t *GetMutex()
    {
        return &m_mutex;
    }

private:
    Mutex(const Mutex&);
    const Mutex &operator = (const Mutex&);

private:
    mutable pthread_mutex_t m_mutex;
    mutable int m_count;
    mutable pthread_t m_owner;
};

class CondMutex
{
public:
    CondMutex()
    {
        int ret = pthread_cond_init(&m_cond, NULL);
        POSIX_ASSERT(ret);
    }

    ~CondMutex()
    {
        int ret = pthread_cond_destroy(&m_cond);
        POSIX_ASSERT(ret);
    }

    int WaitWithTimeout(Mutex *mutex, int mill) const
    {
        struct timespec tv;
        tv.tv_sec = mill / 1000;
        tv.tv_nsec = (mill % 1000) * 1000;
        int ret = pthread_cond_timedwait(&m_cond, mutex->GetMutex(), &tv);
        if (0 != ret && ETIMEDOUT != ret)
        {
            POSIX_ASSERT(ret);
        }

        return ret;
    }

    int Wait(Mutex *mutex) const
    {
        int ret = pthread_cond_wait(&m_cond, mutex->GetMutex());
        if (0 != ret && ETIMEDOUT != ret)
        {
            POSIX_ASSERT(ret);
        }

        return ret;
    }

    void Signal() const
    {
        int ret = pthread_cond_signal(&m_cond);
        POSIX_ASSERT(ret);
    }

private:
    mutable pthread_cond_t m_cond;
};

#endif

class CriticalSectionLock
{
public:
    CriticalSectionLock(Mutex& mx) :m_mutex(mx) { m_mutex.Lock(); }
    ~CriticalSectionLock(){ m_mutex.Unlock(); }

private:
    CriticalSectionLock(const CriticalSectionLock& critical);
    const CriticalSectionLock& operator =(const CriticalSectionLock& critical);
private:
    Mutex& m_mutex;
};

// 如果mx给了一个错误的mutex名字，造成没有锁住，怎么办
#define HONG_CS_LOCK(mx) Hong::CriticalSectionLock __critical_section(mx)
}//end namespace

#endif // HONGMUTEX_H
